home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
FishMarket 1.0
/
FishMarket v1.0.iso
/
fishies
/
101-125
/
disk_107
/
diff
/
diff.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-05-06
|
25KB
|
936 lines
/*
Name: Diff.c
Processor: VAX | MS-DOS
Class: C Program
Creation Date: 1/8/87
Revision:
Author: D. Krantz
Description: File compare and change-bar for text files.
*/
/*
Source: Dr. Dobb's Journal of Software Tools
Issue: #130 August 1987
Article: UTILITIES: What's the DIFF? Page 30.
PD Validation: Page 39, Column 1, Paragraph 6
"I am releasing DIFF into the public domain..."
Amiga Port: Jeffrey Bailey
Revision Date: 10/5/87
Major Changes: All internal tracing and debugging routines removed.
Memory allocation tracking routines added.
CTRL-C trapping added.
Minor bugs fixed.
Linked list routines partially re-written to allow better
usage of memory with a tradeoff in performance.
(storage for lines is dynamically allocated to the
exact size needed)
Added /SOURCE_CODE option.
Added /TRIM & /NOTRIM options.
Changed /UP_CASE to /NOEXACT for better VMS compatibility.
Added register declarations for optimization purposes.
Known Bugs and Problems:
Diff looks for options to start with a forward slash '/'.
If a file spec starts with a forward slash, Diff will
assume it to be an option and will probably generate an
error message. Two possible solutions:
o Always specify file names in such a way that no
leading slashes occur.
o Change OPT_FLAG to another character and re-compile,
losing VMS command line "look and feel".
Options may be abbreviated. For example /SOURCE_CODE may
be abbreviated as /SOURCE. However, if you do not specify
enough characters, the option may be ambiguous. If this
happens, Diff will not issue a warning; the parser will
accept this situation on a first come, first served basis.
This was a problem from the original version. I just didn't
think it was worth the effort to fix it.
Polling for CTRL-C adds overhead. I just didn't know of a
better way to do it. Also, instead of the standard "^C",
this program prints " Interrupt " in reverse video to
maintain the VMS "look and feel". Feel free to change it.
The memory allocation tracking routines add even more
overhead. However, they are necessary because the author
of this program was sloppy about resource tracking (VMS
does it for you, and with MS-DOS it's not a concern).
I couldn't use the system routines AllocRemember() and
FreeRemember() because this program frees individual
chunks of memory at different points in time.
FreeRemember() is an all or nothing process -- unsuitable
for this program. (If I'm incorrect about this, please
feel free to change the source. This is my first major
project on the Amiga and I'm not totally familiar with
the OS yet.)
*/
#include <stdio.h>
#include <ctype.h>
/*
All of these #include's are probably not necessary, but the C compiler
always seems to throw a fit if they aren't in the right order or something.
*/
#include "exec/types.h"
#include "exec/nodes.h"
#include "exec/lists.h"
#include "exec/ports.h"
#include "exec/interrupts.h"
#include "exec/io.h"
#include "exec/memory.h"
#include "libraries/dos.h"
#include "libraries/dosextens.h"
/*
Declaration for my memory routine.
*/
char *TrackMalloc();
/*
Specifies the flag character.
*/
#define OPT_FLAG '/'
#define MAXLINE 2048
#define FORMFEED '\014'
struct LINE {
unsigned long linenum;
unsigned long pagenum;
struct LINE *link;
char *text;
char *dup;
};
typedef struct LINE *line_ptr;
typedef char *char_ptr;
typedef FILE *FILE_PTR;
struct LINE root [3];
FILE_PTR msg;
unsigned long line_count [3] = {1, 1, 1};
unsigned long page_count [3] = {1, 1, 1};
int command_errors = 0;
char xx1 [132], xx2 [132];
int files = 0;
char_ptr infile_name [3] = {NULL, xx1, xx2};
char outfile_name [132];
FILE_PTR infile [3];
FILE *outfile;
static line_ptr at [3] = {NULL, &(root [1]), &(root [2])};
int debug = 0;
int trace_enabled = 0;
int bar_col = 0;
unsigned long top_skip = 0;
unsigned long bot_skip = 0;
unsigned long page_len = 66;
int up_case = 0;
int re_sync = 5;
int output = 0;
int blanks = 0;
int lookahead = 200;
int skip1 = 0;
int skip2 = 0;
int trim_lines = 0;
main(argc, argv)
int argc;
char *argv [];
{
int i;
/* Initialize our file pointers so cleanup() only closes open files. */
i = 0;
while(i < 3)
{
infile [i] = NULL;
i++;
}
outfile = NULL;
if(argc == 1) help();
msg = stdout;
for(i = 1; i < argc; i++) strip_opt(argv [i]);
if(files < 2)
{
printf("DIFF: Error: Must specify two files.\n");
cleanup();
}
open_files();
if(command_errors)
cleanup();
page_skip();
diff();
cleanup();
}
dont_look(line)
register line_ptr line;
{
register char *temp;
check_ctrl_c();
if(line == NULL) return(0);
if(line->linenum <= top_skip) return(1);
if(line->linenum > page_len - bot_skip) return(1);
temp = line->text;
if(!blanks)
{
while(*temp != 0)
{
switch(*temp)
{
case ' ':
case '\t':
case '\n':
break;
default :
return(0);
break;
}
temp++;
}
return(1);
}
return(0);
}
equal(a, b)
line_ptr a, b;
{
check_ctrl_c();
if( (a == NULL) || (b == NULL) ) return(0);
if(up_case) return(!strcmp(a->dup, b->dup));
else return(!strcmp(a->text, b->text));
}
position(f, where)
int f;
line_ptr where;
{
check_ctrl_c();
at [f] = &root [f];
while(at [f]->link != where)
at[f] = at [f]->link;
return(0);
}
char *index(str, c)
register char *str, c;
{
check_ctrl_c();
while(*str != NULL)
{
if(*str == c) return(str);
str++;
}
return(NULL);
}
line_ptr next_line(f)
int f;
{
line_ptr place_hold;
char temp [MAXLINE + 1];
check_ctrl_c();
if( at [f]->link != NULL)
{
at [f] = at [f]->link;
return(at [f]);
}
else
{
at [f]->link = (line_ptr) TrackMalloc(sizeof(struct LINE));
if(at [f]->link == NULL)
{
printf("DIFF: Out of Memory.\n");
cleanup();
}
place_hold = at [f];
at [f] = at [f]->link;
at [f]->link = NULL;
at [f]->text = NULL;
at [f]->dup = NULL;
if(fgets(temp, MAXLINE, infile [f]) == NULL)
{
TrackFree(at [f]);
at [f] = place_hold;
at [f]->link = NULL;
return(NULL);
}
if(trim_lines)
{
trim(temp);
strcat(temp, "\n");
}
at [f]->text = TrackMalloc(strlen(temp) + 1);
if(at [f]->text == NULL)
{
printf("DIFF: Out of Memory.\n");
cleanup();
}
strcpy(at [f]->text, temp);
if( (index( at [f]->text, FORMFEED) != NULL) ||
(line_count [f] > page_len))
{
page_count [f]++;
line_count [f] = 1;
}
at [f]->linenum = line_count [f]++;
at [f]->pagenum = page_count [f];
if(up_case)
{
at [f]->dup = TrackMalloc(strlen(at [f]->text) + 1);
if(at [f]->dup == NULL)
{
printf("DIFF: Out of Memory.\n");
cleanup();
}
strcpy(at [f]->dup, at [f]->text);
upper(at [f]->dup);
}
return(at [f]);
}
}
discard(f, to)
int f;
line_ptr to;
{
register line_ptr temp;
check_ctrl_c();
while(1)
{
if(root [f].link == NULL) break;
temp = root [f].link;
root [f].link = root [f].link->link;
TrackFree(temp->text);
if(temp->dup != NULL) TrackFree(temp->dup);
TrackFree(temp);
if(temp == to) break;
}
at [f] = &root [f];
return(0);
}
vfputs(str, file)
char *str;
FILE *file;
{
check_ctrl_c();
fputs(str, file);
return(0);
}
put(line)
line_ptr line;
{
register line_ptr temp;
if(output)
for(temp = root [1].link; ;)
{
check_ctrl_c();
if(temp == NULL) return(0);
vfputs(temp->text, outfile);
if(temp == line) return(0);
temp = temp->link;
}
return(0);
}
char *change_bar(str)
register char *str;
{
register int i;
char temp [MAXLINE + 1], *base;
check_ctrl_c();
base = temp;
strcpy(temp, str);
i = 0;
if(bar_col != 0)
{
while(*base != '\n')
{
if((*base == '\r') && (*(base + 1) != '\n')) i = 0;
base++;
i++;
}
while(i++ < bar_col) *(base)++ = ' ';
strcpy(base, "|\n");
}
else
{
if(temp [0] != ' ')
{
strcpy(&temp [1], str);
}
temp [0] = '|';
}
TrackFree(str);
base = TrackMalloc(strlen(temp) + 1);
if(base == NULL)
{
printf("DIFF: Out of Memory.\n");
cleanup();
}
strcpy(base, temp);
return(base);
}
added(line)
line_ptr line;
{
register line_ptr temp;
check_ctrl_c();
for(temp = root [1].link; ;)
{
if(temp == NULL) return(0);
if(!dont_look(temp))
fprintf(msg, "+ Page %d : Line %d -> %s", temp->pagenum, temp->linenum,
temp->text);
if(output)
if(dont_look(temp)) vfputs(temp->text, outfile);
else vfputs((temp->text = change_bar(temp->text)), outfile);
if(temp == line) return(0);
temp = temp->link;
}
}
deleted(line)
line_ptr line;
{
register line_ptr temp;
check_ctrl_c();
for(temp = root [2].link; ; )
{
if(temp == NULL) return(0);
if(!dont_look(temp))
fprintf(msg, "- Page %d : Line %d -> %s", temp->pagenum, temp->linenum,
temp->text);
if(temp == line) return(0);
temp = temp->link;
}
return(0);
}
resync(first, second)
line_ptr first, second;
{
register line_ptr file1_start, file2_start;
line_ptr last_bad1, last_bad2, t1, t2;
int i, j, k, moved1, moved2;
check_ctrl_c();
moved1 = 0;
file1_start = first;
position(1, first);
for(k = 0; k < lookahead; k++)
{
while(dont_look(file1_start = next_line(1)));
if(file1_start == NULL) goto no_sy;
moved2 = 0;
file2_start = second;
position(2, second);
for(j = 0; j < lookahead; j++)
{
while(dont_look(file2_start = next_line(2)));
if(file2_start == NULL) goto eof2;
t1 = file1_start;
t2 = file2_start;
for(i = 0; (i < re_sync) && equal(t1, t2); i++)
{
while(dont_look(t1 = next_line(1)));
while(dont_look(t2 = next_line(2)));
if( (t1 == NULL) || (t2 == NULL)) break;
}
if(i == re_sync) goto synced;
last_bad2 = file2_start;
position(2, file2_start);
while(dont_look(file2_start = next_line(2)));
moved2++;
}
eof2:
check_ctrl_c();
last_bad1 = file1_start;
position(1, file1_start);
while(dont_look(file1_start = next_line(1)));
moved1++;
}
printf("DIFF: Lost sync in file %s at page %d line %d.\n",
infile_name [1], first->pagenum, first->linenum);
fclose(outfile);
outfile = NULL;
cleanup();
no_sy:
check_ctrl_c();
position(1, first);
while( (first = next_line(1)) != NULL)
{
added(first);
discard(1, first);
}
return(0);
synced:
check_ctrl_c();
if(moved1)
{
added(last_bad1);
discard(1, last_bad1);
}
position( 1, file1_start);
if(moved2)
{
deleted(last_bad2);
discard(2, last_bad2);
}
position(2, file2_start);
fprintf(msg, "\n");
return(0);
}
diff()
{
register line_ptr first, second;
check_ctrl_c();
while(1)
{
while(dont_look(first = next_line(1)));
if(first == NULL)
{
put(first);
return(0);
}
while(dont_look(second = next_line(2)));
if(equal(first, second))
{
put(first);
discard(1, first);
discard(2, second);
}
else resync(first, second);
if(second == NULL) return(0);
}
}
page_skip()
{
register line_ptr first, second;
check_ctrl_c();
while(1)
{
first = next_line(1);
if( (first == NULL) || (first->pagenum > skip1) ) break;
put(first);
discard(1, first);
}
if(first != NULL) position(1, first);
while(1)
{
second = next_line(2);
if( (second == NULL) || (second->pagenum > skip2) ) break;
discard(2, second);
}
if(second != NULL) position(2, second);
return(0);
}
help()
{
printf("\nDIFF");
printf("\nText File Difference Detector");
printf("\n");
printf("\nFormat:");
printf("\nDIFF [option {option} ...] newfile oldfile [barfile]");
printf("\n");
printf("\n newfile = latest revision of text file.");
printf("\n oldfile = baseline to compare against.");
printf("\n barfile = output file if change bars are desired.");
printf("\n");
printf("\nOptions:");
printf("\n /BAR_COL=n Column of output file in which change bar");
printf("\n will appear. Default = %d", bar_col);
printf("\n /BLANKS Blank lines are to be considered significant.");
printf("\n /NOBLANKS Blank lines are not significant. DEFAULT");
printf("\n /BOT_SKIP=n Lines at bottom of page to skip for running");
printf("\n footers and page numbers. Default = %d",
bot_skip);
printf("\n /EXACT Case is significant. DEFAULT");
printf("\n /NOEXACT Upper/Lower case is not considered");
printf("\n significant in the comparison.");
printf("\n /LOOKAHEAD=n Maximum number of lines to look ahead in");
printf("\n each file to resync after a difference.");
printf("\n Default = %d", lookahead);
printf("\n /OUTPUT=file File to which differences summary is written.");
printf("\n Default is to stdout.");
printf("\n /PAGE_LEN=n Lines per page. (embedded form feeds override");
printf("\n this value) Default = %d", page_len);
printf("\n /RE_SYNC=n Number of lines that must match before the");
printf("\n files are considered synced. Default = %d",
re_sync);
printf("\n /SKIP1=n Pages in NEWFILE to skip before comparison");
printf("\n starts. Also sets /SKIP2. Default = %d",
skip1);
printf("\n /SKIP2=n Pages in OLDFILE to skip before comparison.");
printf("\n MUST BE AFTER /SKIP1 ! Default = %d", skip2);
printf("\n /SOURCE_CODE Causes DIFF to stop counting lines for page");
printf("\n breaks. Everything is considered to be");
printf("\n on page 1. (embedded form feeds override)");
printf("\n /TOP_SKIP=n Lines at top of page to skip for running");
printf("\n headings and page numbers. Default = %d",
top_skip);
printf("\n /TRIM_LINES Trim trailing spaces, tabs, returns, and");
printf("\n newlines from the lines before comparison.");
printf("\n /NOTRIM_LINES Do not trim lines before comparison. DEFAULT");
printf("\n\n");
}
open_files()
{
int i;
check_ctrl_c();
for(i = 1; i < 3; i++)
if( (infile [i] = fopen(infile_name [i], "r")) == NULL)
{
printf("DIFF: Error: Can't open %s for READ!\n", infile_name [i]);
command_errors++;
}
if(files > 2)
if( (outfile = fopen(outfile_name, "w")) == NULL)
{
printf("DIFF: Error: Can't open %s for WRITE!\n", outfile_name);
command_errors++;
}
return(0);
}
redirect(str)
char *str;
{
char filename [132];
register char *ptr, *dest;
check_ctrl_c();
dest = filename;
if( (ptr = index(str, '=') + 1) == (char *) (NULL + 1) )
{
printf("DIFF: Error in option %s.\n", str);
command_errors++;
}
while( (*(dest++) = *(ptr++)) != '\0');
*dest = '\0';
if( (msg = fopen(filename, "w")) == NULL)
{
printf("DIFF: Can't open %s for WRITE!\n", filename);
command_errors++;
}
return(0);
}
strip_opt(str)
char *str;
{
check_ctrl_c();
upper(str);
if( *str == OPT_FLAG)
{
if(match(str + 1, "BAR_COL"))
bar_col = num(str);
else if(match(str +1, "TOP_SKIP"))
top_skip = num(str);
else if(match(str +1, "BOT_SKIP"))
bot_skip = num(str);
else if(match(str + 1, "PAGE_LEN"))
page_len = num(str);
else if(match(str + 1, "NOEXACT"))
up_case = 1;
else if(match(str + 1, "EXACT"))
up_case = 0;
else if(match(str + 1, "RE_SYNC"))
re_sync = num(str);
else if(match(str + 1, "BLANKS"))
blanks = 1;
else if(match(str + 1, "NOBLANKS"))
blanks = 0;
else if(match(str + 1, "LOOKAHEAD"))
lookahead = num(str);
else if(match(str + 1, "SKIP1"))
skip1 = skip2 = num(str);
else if(match(str + 1, "SKIP2"))
skip2 = num(str);
else if(match(str + 1, "OUTPUT"))
redirect(str);
else if(match(str + 1, "SOURCE_CODE"))
page_len = 2147483647L;
else if(match(str + 1, "TRIM_LINES"))
trim_lines = 1;
else if(match(str + 1, "NOTRIM_LINES"))
trim_lines = 0;
else
{
printf("DIFF: Unrecognized Option: %s.\n", str);
command_errors++;
}
}
else
{
switch(files)
{
case 0:
strcpy(infile_name [1], str);
break;
case 1:
strcpy(infile_name [2], str);
break;
case 2:
strcpy(outfile_name, str);
output = 1;
break;
default :
printf("DIFF: Error: Too many files specified at %s\n", str);
command_errors++;
break;
}
files++;
}
return(0);
}
upper(str)
register char *str;
{
check_ctrl_c();
while(*str != NULL)
{
*str = toupper(*str);
str++;
}
}
int match(str, pattern)
register char *str, *pattern;
{
check_ctrl_c();
while(1)
{
if(*str != *pattern) return(0);
str++;
pattern++;
if(*pattern == '\0') return(1);
if(*str == '\0') return(1);
if(*str == '=') return(1);
}
}
int num(str)
char *str;
{
register char *temp;
check_ctrl_c();
temp = index(str, '=');
if(temp == NULL) return(0);
else return(atoi(temp + 1));
}
cleanup()
{
int i;
/*
Unnecessary, but a good habit to get into. Added when I was looking
for a creeping memory loss.
*/
i = 0;
while(i < 3)
{
if(infile [i] != NULL) fclose(infile [i]);
infile [i] = NULL;
i++;
}
if(outfile != NULL) fclose(outfile);
outfile = NULL;
/* Free up any memory that's not been freed yet. Necessary. */
TrackFreeAll();
exit();
}
/*
Node structure for our linked list that keeps track of memory allocations.
This wouldn't be necessary if the original program wasn't so sloppy
about it's allocations and deallocations. Note that this was added
so the program wouldn't eat memory when run on an Amiga. (no resource
tracking, ya know!) Also note that I couldn't use the native routines
such as AllocRemember() on the Amiga, 'cause with them it's all or nothing,
and this program selectively frees memory at odd times.
*/
struct TrackNode {
struct TrackNode *next;
char *address;
unsigned long size;
};
/*
Base pointer for our linked list. Also an end-of-list pointer so we
don't have to scan the entire list to find the end in order to add a
new node. Saves lots of time!
*/
static struct TrackNode *TrackBase = NULL;
static struct TrackNode *TrackLEnd = NULL;
char *TrackMalloc(msize)
unsigned long msize;
{
register struct TrackNode *temp, *current;
char *memory;
temp = (struct TrackNode *) AllocMem(sizeof(struct TrackNode), MEMF_PUBLIC);
if(temp == NULL) return(NULL);
memory = (char *) AllocMem(msize, MEMF_PUBLIC);
if(memory == NULL)
{
FreeMem(temp, sizeof(struct TrackNode));
return(NULL);
}
if(TrackBase == NULL)
{
TrackBase = temp;
TrackBase->next = NULL;
TrackBase->address = memory;
TrackBase->size = msize;
TrackLEnd = TrackBase;
}
else
{
current = TrackLEnd;
current->next = temp;
current = current->next;
current->next = NULL;
current->address = memory;
current->size = msize;
TrackLEnd = current;
}
return(memory);
}
/*
Unfortunately, in this case we must scan the list. This routine is
probably relatively slow, although I optimized it as much as possible.
*/
TrackFree(pointer)
register char *pointer;
{
register struct TrackNode *current, *prev;
current = TrackBase;
if(current == NULL)
{
return(NULL);
}
prev = TrackBase;
while(current->next != NULL && current->address != pointer)
{
prev = current;
current = current->next;
}
if(current->address != pointer)
{
return(NULL);
}
if(current == TrackBase)
{
TrackBase = current->next;
}
else
{
prev->next = current->next;
}
FreeMem(current->address, current->size);
FreeMem(current, sizeof(struct TrackNode));
if(current == TrackLEnd) TrackLEnd = prev;
return(1);
}
/*
Free up all memory that's still allocated.
*/
TrackFreeAll()
{
register struct TrackNode *current, *hold;
current = TrackBase;
while(current != NULL)
{
FreeMem(current->address, current->size);
hold = current->next;
FreeMem(current, sizeof(struct TrackNode));
current = hold;
}
TrackBase = NULL;
TrackLEnd = NULL;
return(1);
}
trim(start)
char *start;
{
int slen;
register char *end;
slen = strlen(start);
if(slen == 0) return(0);
end = start + slen - 1;
while( (*end == ' ' || *end == '\t' || *end == '\n' || *end == '\r')
&& end >= start) end--;
end++;
*end = '\0';
}
ctrl_c_pressed()
{
if(SetSignal(0l, 0l) & SIGBREAKF_CTRL_C)
{
return(1);
}
SetSignal(0l, SIGBREAKF_CTRL_C);
return(NULL);
}
check_ctrl_c()
{
if(ctrl_c_pressed())
{
printf("\n\033[7m Interrupt \033[m\n\n");
cleanup();
}
}